package com.multimedia.scala.euler

import scala.actors.Actor
import scala.annotation.tailrec
import scala.collection.mutable.ListBuffer
import scala.concurrent.MailBox
import com.multimedia.scala.Primes
/**
 * this problem is related to problem 108
 */
object Asd110 {
    def main(args : Array[String]) : Unit = {
        val simples = Primes.findSimples(100000).take(17)//.map(BigInt(_))
        //val min = simples.take(7).reduce(_*_)
        //val max = simples.map(BigInt(_)).reduce(_*_)
        //val max = 3227119050144990l 
        //println(max)
        //val digits = generate(min, max, 38759, simples.toList.map(BigInt(_)))
        //println(digits.size)
        //println(func1(15)) has more then
    	//println(simples.reduce(_*_))
        val tasks = new MailBox()
        val results = new MailBox()
        //val slaves = (1 to 1).map(x=>new Slave(tasks, results)).toList
        //findResults(2*2*2*2*3*3*3*5*5, slaves, tasks, results)
        //findResults(simples.take(9).map(BigInt(_)).reduce(_*_), slaves, tasks, results)
        //println(digit(List(2, 2, 1, 1, 1, 1), simples))
        
        //val candidates = generate2(8)
        val candidates = List(List(3,3,2,2), List(2, 2))
        //.foreach(println)
        .map(x=>x->digit(x, simples))
        //candidates.foreach(println)
        println("calculated="+candidates.size)
        var min = BigInt(10).pow(20)
        candidates
        .sort((x, y)=>(x._2<y._2))
        .map(x=> {
            val z = new MyBean(x._1, x._2, findResults(x._2, (1 to 4).map(x=>new Slave(tasks, results)).toList, tasks, results))
            val rez = func1(4000000, z.solutions)
            val newFactor = enlarge(z.factor, rez._2)
            val newDigit = digit(newFactor, simples)
            if (min>newDigit) {
                min = newDigit
            	println("-->"+z->new MyBean(newFactor, newDigit, rez._1))
            } else {
            	println(z->new MyBean(newFactor, newDigit, rez._1))
            }
        })
    }
    case class MyBean(factor: List[Int], digit: BigInt, solutions: Int)
    @tailrec
    def func1(max: Int, solutions: Int = 2, n: Int = 1): (Int, Int) = {
        if (max<=solutions) return (solutions, n)
        func1(max, solutions*3-1, n+1)
    }
    @tailrec
    def enlarge(list: List[Int], n: Int): List[Int] = {
        if (n==1) return list
        else enlarge(list:+1, n-1)
    }
    /** create a digit from an array of powers of primes */
    def digit(list: List[Int], simples: ListBuffer[Int]): BigInt = {
       var rez = BigInt(1)
       for (i <- 0 to list.size-1) {
           for (p <- 1 to list(i)) {
               rez = rez * simples(i)
           }
       }
       return rez
    }

    def generate2(primes: Int): List[List[Int]] = {
        var acc = ListBuffer[Int]()
        (1 to primes).foreach(x=>acc+=1)
        var rez = ListBuffer[List[Int]](acc.toList)
        def next(i: Int = 0): Boolean = {
            if (i==primes) return false
            if (acc(i)==primes-i) {
                acc(i) = 1
                return next(i+1)
            } else {
                acc(i)+=1
                return true
            }
        }
        def check(list: List[Int]): Boolean = {
            for (i<-1 to list.size-1)
              if (list(i-1)<list(i))
                return false
            return true
        }
        while(next()) { 
            rez += acc.toList
        }
        
        return rez.toList.filter(check).map(_.takeWhile(_!=1))
    }

    def generate(min: BigInt, max: BigInt, quantity: Int, list: List[BigInt]): List[BigInt] = {
        def recurse(last: Int, acc: ListBuffer[BigInt]): ListBuffer[BigInt] = {
            if (acc.last>max||acc.size>=quantity) return acc
            if (last==acc.size) return acc
            println(acc.size, acc.last)
            val size = acc.size-1
            for (i <- last to size) {
              list.foreach(x=>{
                  val y = x*acc(i)
                  acc+=y
              })
            }
            
            return recurse(size+1, acc.distinct.filter(max>))
        }
        return recurse(0, ListBuffer[BigInt]()++(list.map(_*min))).toList
    }

    def findResults(n: BigInt, slaves: List[Slave], tasks: MailBox = new MailBox(), results: MailBox = new MailBox()) = {
    	val size = n/slaves.size+1
    	var rez = 0
        slaves.foreach(x=>{x.start()})
        for (i<-1 to slaves.size-1) {
            tasks send Count(n, n+size*(i-1), n+size*i)
        }
        tasks send Count(n, n+size * (slaves.size-1), n * 2)
        (1 to slaves.size).foreach(x=>
            results receive {
              case Result(solutions, n) => {
                  rez+=solutions
              }
            }
        )
        //println("calculation ended")
        slaves.foreach(x=>tasks send Stop)
        //println(rez)
        rez
    }
}
/** finds all results for given n with x from min to max */ 
class Slave(tasks: MailBox, results: MailBox) extends Actor {
    @tailrec
    private def findRez(n: BigInt, x: BigInt, max: BigInt, acc: Int = 0): Int = {
        if (x > max) return acc
        if ((n*x)%(x-n)==0){
            findRez(n, x+1, max, acc+1)
        } else findRez(n, x+1, max, acc)
    }
    def act() {
        loop {
            tasks receive {
                case Count(n, min, max) => {
                    val rez = findRez(n, min+1, max)
                    results send Result(rez, n)
                }
                case Stop => exit()
            }
        }
    }
}
 
    case class Count(n: BigInt, min: BigInt, max: BigInt)
    case class Result(solutions: Int, n: BigInt)
    case class Stop
